﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace SocketDemo
{
    public partial class MainForm : Form
    {
        List<Socket> clientProxSocketList = new List<Socket>();

        public MainForm()
        {
            InitializeComponent();
        }

        private void btn_Start_Click(object sender, EventArgs e)
        {
            // 创建Socket对象
            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            // 绑定IP地址和端口
            socket.Bind(new IPEndPoint(IPAddress.Parse(txt_IP.Text), int.Parse(txt_Port.Text)));

            // 开启侦听
            socket.Listen(10); // 10 指的是连接等待队列的最大长度

            // 开始接受客户端连接 （将这种会阻塞整个程序执行的操作，放到线程池中）
            ThreadPool.QueueUserWorkItem(new WaitCallback(this.AcceptClientConnect), socket);

        }

        public void AcceptClientConnect(object socket)
        {
            var serverSocket = socket as Socket;

            this.AppendTextToTxtLog("服务器端开始接受客户端连接请求");

            while(true)
            {
                var proxSocket = serverSocket.Accept();
                this.AppendTextToTxtLog(string.Format("客户端：{0}连接成功", proxSocket.RemoteEndPoint.ToString()));
                clientProxSocketList.Add(proxSocket);
                // 持续接受客户端发来的消息
                ThreadPool.QueueUserWorkItem(new WaitCallback(ReceiveData), proxSocket);
            }
        }

        // 接收消息
        public void ReceiveData(object socket)
        {
            var proxSocket = socket as Socket;
            byte[] data = new byte[1024*1024];
            while (true)
            {
                // 不断接受客户端传来的消息，并更新到聊天框上面
                int len = 0;
                try
                {
                    len = proxSocket.Receive(data, 0, data.Length, SocketFlags.None);
                }
                catch (Exception ex)
                {
                    // 异常退出
                    AppendTextToTxtLog(string.Format("客户端{0}异常退出", proxSocket.RemoteEndPoint.ToString()));
                    clientProxSocketList.Remove(proxSocket);
                    // 关闭与指定客户端的连接
                    StopConnect(proxSocket);

                    return;
                }
                if(len <= 0)
                {
                    // Client exited
                    AppendTextToTxtLog(string.Format("客户端{0}正常退出", proxSocket.RemoteEndPoint.ToString()));
                    clientProxSocketList.Remove(proxSocket);
                    // 关闭与指定客户端的连接
                    StopConnect(proxSocket);

                    return; // 让方法结束，即结束当前线程
                }
                string txt = Encoding.Default.GetString(data, 0, len);
                AppendTextToTxtLog(string.Format("接收到客户端：{0}的消息是：{1}", proxSocket.RemoteEndPoint.ToString(), txt));
            }
        }

        private void StopConnect(Socket proxSocket)
        {
            try
            {
                if (proxSocket.Connected)
                {
                    proxSocket.Shutdown(SocketShutdown.Both);
                    proxSocket.Close(100);
                }
            }
            catch (Exception ex)
            {

            }
        }

        #region 发送字符串
        // 发送消息
        private void btn_SendMessage_Click(object sender, EventArgs e)
        {
            foreach (Socket proxSocket in clientProxSocketList)
            {
                if (proxSocket.Connected)
                {
                    // 字符串的原始字节数组
                    byte[] data = Encoding.Default.GetBytes(txt_Message.Text);
                    // 给原始的字符串字节数组加上表示头（1个字节，用来标识是什么类型的数据）
                    byte[] result = new byte[data.Length + 1];
                    result[0] = 1; // 最终字节数组的第一个字节为1，代表该数据类型为字符串
                    Buffer.BlockCopy(data, 0, result, 1, data.Length);
                    proxSocket.Send(result, 0, result.Length, SocketFlags.None);
                }
            }
        } 
        #endregion

        // 往聊天框上追加数据


        #region 发送闪屏
        private void btn_SendShake_Click(object sender, EventArgs e)
        {
            foreach (Socket proxSocket in clientProxSocketList)
            {
                if (proxSocket.Connected)
                {
                    proxSocket.Send(new byte[] { 2 }, SocketFlags.None);
                }
            }
        }
        #endregion

        #region 将信息添加到消息框
        public void AppendTextToTxtLog(string txt)
        {
            // 判断当前线程是否是主线程（UI线程）避免跨线程操作
            if (txt_Log.InvokeRequired)
            {
                txt_Log.Invoke(new Action<string>(s =>
                {
                    this.txt_Log.Text = string.Format("{0}\r\n{1}", s, txt_Log.Text);
                }), txt);
            }
            else
            {
                this.txt_Log.Text = string.Format("{0}\r\n{1}", txt, txt_Log.Text);
            }
        }
        #endregion

        private void btn_SendFile_Click(object sender, EventArgs e) // 发送文件
        {
            using (OpenFileDialog dlg = new OpenFileDialog())
            {
                if(dlg.ShowDialog() != DialogResult.OK)
                {
                    return;
                }

                byte[] data = File.ReadAllBytes(dlg.FileName);
                byte[] result = new byte[data.Length + 1];
                result[0] = 3;
                Buffer.BlockCopy(data, 0, result, 1, data.Length);

                foreach (Socket proxSocket in clientProxSocketList)
                {
                    if (proxSocket.Connected)
                    {
                        proxSocket.Send(result, 0, result.Length, SocketFlags.None);
                    }
                }
            }
        }
    }
}
